/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// TGUI - Texus' Graphical User Interface
// Copyright (C) 2012-2025 Bruno Van de Velde (vdv_b@tgui.eu)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef TGUI_THEME_HPP
#define TGUI_THEME_HPP

#include <TGUI/Loading/ThemeLoader.hpp>
#include <TGUI/Renderers/WidgetRenderer.hpp>

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace tgui
{
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// @brief This class can be used to manage the widget renderers
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    class TGUI_API Theme
    {
    public:

        using Ptr = std::shared_ptr<Theme>; //!< Shared widget pointer
        using ConstPtr = std::shared_ptr<const Theme>; //!< Shared constant widget pointer

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Constructs the theme class, with an optional theme file to load
        ///
        /// @param primary  Primary parameter for the theme loader (filename of the theme file in DefaultThemeLoader)
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Theme(const String& primary = "");

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Copy constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Theme(const Theme&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Move constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Theme(Theme&&) noexcept;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Destructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual ~Theme();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Overload of copy assignment operator
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Theme& operator=(const Theme&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Move assignment
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Theme& operator=(Theme&&) noexcept;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Constructs a new theme, with an optional theme file to load
        ///
        /// @param primary  Primary parameter for the theme loader (filename of the theme file in DefaultThemeLoader)
        ///
        /// @return The new theme
        ///
        /// Unlike with widgets, it is not required to use a smart pointer for the theme. You do not have to use this function
        /// to create a theme.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static Theme::Ptr create(const String& primary = "");

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the primary theme loader parameter
        ///
        /// @param primary  Primary parameter for the theme loader (filename of the theme file in DefaultThemeLoader)
        ///
        /// When the theme was loaded before and a renderer with the same name is encountered, the widgets that were using
        /// the old renderer will be reloaded with the new renderer.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void load(const String& primary);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Replaced this theme with another one, while updating all connected widgets to the new renderers
        ///
        /// @param otherTheme  The theme to copy
        ///
        /// The renderers are copied, meaning that all widgets connected to the other theme will remain connected to it.
        /// Any widgets connected to this theme will however be updated with new renderers when the same name is encountered.
        ///
        /// @since TGUI 1.2
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void replace(const Theme& otherTheme);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Gets data for the renderers
        ///
        /// @param id  The secondary parameter for the theme loader (name of section in theme file in DefaultThemeLoader).
        ///
        /// @return Shared renderer data
        ///
        /// @throw Exception if theme loader fails to load the requested renderer
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::shared_ptr<RendererData> getRenderer(const String& id);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Gets data for the renderers
        ///
        /// @param id  The secondary parameter for the theme loader (name of section in theme file in DefaultThemeLoader).
        ///
        /// @return Shared renderer data
        ///
        /// Unlike getRenderer which throws an exception, this function will return nullptr when the renderer is not found
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::shared_ptr<RendererData> getRendererNoThrow(const String& id);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the value of a global property in the theme
        ///
        /// @param property  Name of the global property to retrieve
        ///
        /// @return Value of the global property, or an object with type ObjectConverter::Type::None if no such propery exists
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD ObjectConverter getGlobalProperty(const String& property);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Manually adds a renderer data to the theme
        ///
        /// @param id       Identifier of the renderer
        /// @param renderer The renderer to add
        ///
        /// If a renderer with the same id already exists then it will be replaced by this one.
        /// Widgets using the old renderer will keep using that old renderer and remain unchanged.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void addRenderer(const String& id, std::shared_ptr<RendererData> renderer);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Manually removes a renderer to the theme
        ///
        /// @param id  Identifier of the renderer
        ///
        /// @return True when removed, false when the identifier did not match any renderer
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool removeRenderer(const String& id);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the primary theme loader parameter
        /// @return Primary parameter for the theme loader (filename of the theme file in DefaultThemeLoader)
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD const String& getPrimary() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the function that will load the widget theme data
        ///
        /// @param themeLoader  Pointer to the new loader
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        static void setThemeLoader(std::shared_ptr<BaseThemeLoader> themeLoader);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the function that is currently being used to load the widget theme data
        ///
        /// @return  Theme loader
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static std::shared_ptr<BaseThemeLoader> getThemeLoader();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sets the theme class that widgets use by default
        ///
        /// @param primary  Primary parameter for the theme loader (filename of the theme file in DefaultThemeLoader)
        ///
        /// Calling this function is equivalent to the following:
        /// @code
        /// setDefault(tgui::Theme::create(primary))
        /// @endcode
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        static void setDefault(const String& primary = "");

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sets the theme class that widgets use by default
        ///
        /// @param theme  Theme to use as default
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        static void setDefault(std::shared_ptr<Theme> theme);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Resets the theme that widgets use by default
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        static void setDefault(std::nullptr_t);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns a pointer to the theme class that widgets use by default
        ///
        /// @return Default theme
        ///
        /// When setDefault was not called or was given a nullptr as parameter, getDefault will create and return the default
        /// "White" theme.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static std::shared_ptr<Theme> getDefault();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Inform the theme that a custom renderer class inherits its properties from an existing renderer
        ///
        /// @param widgetType Type name of the custom widget
        /// @param parentType Type name of the base widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        static void addRendererInheritanceParent(const String& widgetType, const String& parentType);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the parent type of a widget
        /// @param widgetType  Type name of the derived widget
        /// @return Type name of the base widget, or an empty string if the widget has no known parent.
        /// @see addRendererInheritanceParent
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static String getRendererInheritanceParent(const String& widgetType);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Inform the theme that a custom renderer has subwidgets that need a default value
        ///
        /// @param widgetType         Type name of the custom widget
        /// @param property           Property of the renderer which should refer to another section in the renderer
        /// @param propertyWidgetType Type name of subwidget, which specifies the section in the theme file to refer to
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        static void addRendererDefaultSubwidget(const String& widgetType, const String& property, const String& propertyWidgetType);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the defaulted subwidgets that a renderer has
        /// @param widgetType  Type name of the widget of which we are looking up the properties
        /// @return Map containing property names as keys the corresponding renderer sections as values
        /// @see addRendererDefaultSubwidget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static std::map<String, String> getRendererDefaultSubwidgets(const String& widgetType);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Inform the theme that a custom renderer has properties that can use a default value from the global properties
        ///
        /// @param widgetType     Type name of the custom widget
        /// @param property       Property of the renderer which refers to the global property
        /// @param globalProperty Global property in the theme that would be copied as default value for the renderer property
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        static void addRendererInheritedGlobalProperty(const String& widgetType, const String& property, const String& globalProperty);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the properties which a renderer inherits from the global theme properties
        /// @param widgetType  Type name of the widget of which we are looking up the properties
        /// @return Map containing renderer property names as keys the corresponding global property names as values
        /// @see addRendererInheritedGlobalProperty
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static std::map<String, String> getRendererInheritedGlobalProperties(const String& widgetType);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        static std::map<String, String> m_rendererInheritanceParents;
        static std::map<String, std::map<String, String>> m_rendererDefaultSubwidgets;
        static std::map<String, std::map<String, String>> m_rendererInheritedGlobalProperties;
        static std::shared_ptr<Theme> m_defaultTheme;
        static std::shared_ptr<BaseThemeLoader> m_themeLoader;  //!< Theme loader which will do the actual loading

        std::map<String, std::shared_ptr<RendererData>> m_renderers; //!< Maps ids to renderer datas
        std::map<String, ObjectConverter> m_globalProperties; //!< Maps id to value
        String m_primary;
    };

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#endif // TGUI_THEME_HPP
